import { Vector3 } from '@babylonjs/core/Maths/math.vector';
import { DefaultStage } from '../default-stage';
import { Utils } from 'babylon-lib/tool/utils';
import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
import { InstancedMesh } from '@babylonjs/core/Meshes/instancedMesh';
import { Mesh } from '@babylonjs/core/Meshes/mesh';
import { Color3 } from '@babylonjs/core/Maths/math.color';
import { MeshBuilder } from '@babylonjs/core/Meshes/meshBuilder';
import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial';
import { ICommunityData } from '../../data/community';
import { BLOC_INFOS } from '../../global/global-constant';

/**
 * 建筑可选参数
 * @param name 名称
 * @param position 位置
 * @param modelUrl 模型路径
 * @param sovereign 主权归属
 * @param tagOffset 标签偏移
 * @param flagOffset 旗帜偏移
 * @param flagSize 旗帜大小
 * @param population 人口
 */
interface IAbstractCommunityOptions {
    name: string,
    position: Vector3,
    modelUrl: string,
    sovereign?: string | null,
    tagOffset?: Vector3,
    flagOffset?: Vector3,
    flagSize?: number,
    population?: number,
}

/**
 * 建筑类型
 * @member CITY 城
 * @member TOWN 镇
 * @member FORTRESS 关塞
 * @member VILLAGE 邑
 */
enum COMMUNITY_TYPE {
    CITY = 'city',
    TOWN = 'town',
    FORTRESS = 'fortress',
    VILLAGE = 'village'
}

/**
 * 抽象建筑类
 */
abstract class AbstractCommunity {

    public readonly id = Utils.id;
    public readonly name: string;
    public readonly stage: DefaultStage;
    public readonly container: TransformNode;

    public readonly pickMesh: Mesh;

    protected options: IAbstractCommunityOptions;
    protected flag: InstancedMesh;
    protected tagContainer: TransformNode;

    private _flagColor = '#000000';
    private _dataIndex!: number;

    private static _flag: Mesh;
    private static _initializeFlagMesh(stage: DefaultStage): void {
        if (this._flag) return;
        const m = MeshBuilder.CreatePlane('communityFlagMesh', { size: 1 }, stage.mainScene);
        m.registerInstancedBuffer('color', 3);
        m.isPickable = false;
        m.isVisible = false;

        const material = new StandardMaterial('communityFlagMaterial', stage.mainScene);
        material.disableLighting = true;
        material.emissiveColor = Color3.FromHexString('#ffffff');
        m.material = material;
        this._flag = m;
    }

    constructor(stage: DefaultStage, options: IAbstractCommunityOptions) {
        AbstractCommunity._initializeFlagMesh(stage);
        this.options = this.initializeOptions(options);
        this.name = this.options.name;
        this.stage = stage;
        this.container = new TransformNode(this.name + 'Container', stage.mainScene);
        this.container.position.copyFrom(this.options.position);
        this.tagContainer = new TransformNode(this.name + 'TopContainer', stage.mainScene);
        this.tagContainer.parent = this.container;
        this.tagContainer.position.copyFrom(this.options.tagOffset!);
        this.tagContainer.billboardMode = TransformNode.BILLBOARDMODE_ALL;
        this.setModel();
        this.pickMesh = this.initializePickMesh();
        this.pickMesh.parent = this.container;
        this.flag = this.initializeFlag();
    }

    public abstract get type(): COMMUNITY_TYPE;

    public abstract get data(): ICommunityData;

    public set dataIndex(dataIndex: number) {
        this._dataIndex = dataIndex;
    }

    public get dataIndex(): number {
        return this._dataIndex;
    }

    public get sovereign(): string | undefined | null {
        return this.options.sovereign;
    }

    public get flagColor(): string {
        return this._flagColor;
    }

    public setSovereign(sovereign?: string | null): void {
        this.options.sovereign = sovereign;
        for (const bc of BLOC_INFOS) {
            if (bc.name === sovereign) {
                this._flagColor = bc.color;
                (this.flag.instancedBuffers.color as Color3) = Color3.FromHexString(this._flagColor);
                break;
            }
        }
    }

    protected initializeOptions(options: IAbstractCommunityOptions): IAbstractCommunityOptions {
        const ops = options;
        return {
            name: ops.name,
            position: ops.position,
            modelUrl: ops.modelUrl,
            sovereign: ops.sovereign,
            tagOffset: ops.tagOffset || new Vector3(0, 0.5, 0),
            flagOffset: ops.flagOffset || new Vector3(0, 0, 0),
            flagSize: ops.flagSize || 1,
            population: ops.population || 0,
        };
    }

    private setModel(): void {
        const model = this.stage.loader(this.stage.mainScene).getMeshTaskInfo(this.options.modelUrl)!;
        model.loadedMeshes?.forEach(m => {
            m.isPickable = false;
            if (m.id !== '__root__') {
                const ins = new InstancedMesh(m.name + this.id, m as Mesh);
                ins.scaling.setAll(0.2);
                ins.parent = this.container;
                ins.position.set(0, 0, 0);
                ins.isPickable = false;
            }
        });
    }

    protected initializeFlag(): InstancedMesh {
        const m = AbstractCommunity._flag.createInstance(this.id + 'FlagMesh');
        m.instancedBuffers.color = Color3.FromHexString(this._flagColor);
        m.alwaysSelectAsActiveMesh = true;
        m.isVisible = true;
        m.parent = this.tagContainer;
        m.scaling.setAll(this.options.flagSize!);
        m.position.copyFrom(this.options.flagOffset!);
        m.isPickable = false;
        return m;
    }

    protected initializePickMesh(): Mesh {
        const m = MeshBuilder.CreateBox(this.name + 'PickMesh', { size: 1 }, this.stage.mainScene);
        m.parent = this.container;
        return m;
    }

}


export { AbstractCommunity, COMMUNITY_TYPE };
export type { IAbstractCommunityOptions };